﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;

/*
 * HOMEWORK 3: Silverlight Calculator
 * A calculator is really just a stack. When a number is pushed onto the stack, it must be followed by an operator.
 * When the equals sign is pushed on, the stack is unwound and the operators are applied in order. We're not getting
 * into nested operations here. No time :(
 * Features:
 * 1. Basic math
 * 2. Memory functions
 * 3. Financial functions
 * 4. Toggle display of tape
 */
namespace Calculator
{
    public partial class Calc : UserControl
    {
        string operators = "+-/*";
        Stack<string> input;
        double memory;
        bool hasBeenTotaled;
        const int MAX_WIDTH = 550;
        const int MIN_WIDTH = 410;
        const int MAX_CALC_HEIGHT = 187;
        const int MIN_CALC_HEIGHT = 164;
        const string MEMORY_RECALL = "MR";
        const string EQUALS = "=";

        public Calc()
        {
            // Required to initialize variables
            
            InitializeComponent();

            lstTape.Visibility = Visibility.Collapsed;
            this.Width = MIN_WIDTH;
            hasBeenTotaled = false;
            input = new Stack<string>();
            memory = 0;

            grdButtons.Height = MAX_CALC_HEIGHT;
            grdButtons.Margin = new Thickness(0, 0, 0, 0);
            pnlFinancial.Visibility = Visibility.Collapsed;

        }

        #region Output

        private void RenderOutput(string val, bool toTape)
        {
            if (!string.IsNullOrEmpty(val) && toTape)
            {
                RenderTape(val);
            }
            txtDisplay.Text = val.Replace(EQUALS, "").Replace(MEMORY_RECALL, "");           
        }

        private void RenderOutput(string val)
        {
            RenderOutput(val, false);
        }

        private void RenderTape(string val)
        {
            ListBoxItem item = new ListBoxItem();
            item.Content = val;
            item.HorizontalAlignment = HorizontalAlignment.Right;
            lstTape.Items.Add(item);
            lstTape.ScrollIntoView(item);
        }

        private void btnBack_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(txtDisplay.Text.Substring(0, txtDisplay.Text.Length - 2));
        }

        private void btnClearEverything_Click(object sender, RoutedEventArgs e)
        {
            memory = 0;
            input.Clear();
            Clear();
        }

        private void btnClear_Click(object sender, RoutedEventArgs e)
        {
            Clear();   
        }

        private void Clear()
        {
            txtRate.Text = "";
            txtDeposit.Text = "";
            txtBorrowed.Text = "";
            txtYears.Text = "";
            RenderOutput("");
        }
        private void ToggleTape(object sender, RoutedEventArgs e)
        {
            if (lstTape.Visibility == Visibility.Collapsed)
            {
                this.Width = MAX_WIDTH;
                lstTape.Visibility = Visibility.Visible;
            }
            else
            {
                this.Width = MIN_WIDTH;
                lstTape.Visibility = Visibility.Collapsed;
            }
        }

        #endregion

        #region Memory

        private void btnMemAdd_Click(object sender, RoutedEventArgs e)
        {
            double.TryParse(txtDisplay.Text, out memory);
        }

        private void btnMemRecall_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(MEMORY_RECALL + " " + memory.ToString(), true);
        }

        private void btnMemClear_Click(object sender, RoutedEventArgs e)
        {
            memory = 0;
        }

        #endregion

        #region Operators

        private void btnSubtract_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(txtDisplay.Text, true);
            input.Push(txtDisplay.Text);
            input.Push("-");
            RenderTape("-");
            RenderOutput("");
        }

        private void btnDivide_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(txtDisplay.Text, true);
            input.Push(txtDisplay.Text);
            input.Push("/");
            RenderTape("/");
            RenderOutput("");
        }

        private void btnSqr_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(txtDisplay.Text, true);
            RenderTape("sqrt");
            RenderOutput(Math.Sqrt(double.Parse(txtDisplay.Text)).ToString());
        }

        private void btnMult_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(txtDisplay.Text, true);
            input.Push(txtDisplay.Text);
            input.Push("*");
            RenderTape("*");
            RenderOutput("");
        }

        private void btnPct_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput((double.Parse(txtDisplay.Text) * .01).ToString());
        }

        private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput(txtDisplay.Text, true);
            input.Push(txtDisplay.Text);
            input.Push("+");
            RenderTape("+");
            RenderOutput("");
        }

        private void btnEquals_Click(object sender, RoutedEventArgs e)
        {
            RenderTape(txtDisplay.Text);

            input.Push(txtDisplay.Text.Replace("=", ""));

            Stack<string>.Enumerator stackRunner = input.GetEnumerator();
            string curVal = "";
            string currentOperator = "";
            double val = 0;
            while (stackRunner.MoveNext())
            {

                if (String.IsNullOrEmpty(stackRunner.Current))
                {
                    continue;
                }

                if (operators.Contains(stackRunner.Current))
                {
                    currentOperator = stackRunner.Current;
                }
                else
                {
                    currentOperator = "";
                    val = Double.Parse(stackRunner.Current);
                }

                if (!String.IsNullOrEmpty(currentOperator))
                {
                    stackRunner.MoveNext();
                    double nextVal = double.Parse(stackRunner.Current);
                    switch (currentOperator)
                    {
                        case "+":
                            {
                                val += nextVal;
                                break;
                            }
                        case "-":
                            {
                                val = nextVal - val;
                                break;
                            }
                        case "*":
                            {
                                val *= nextVal;
                                break;
                            }
                        case "/":
                            {
                                if (val != 0)
                                {
                                    val = nextVal / val;
                                }
                                else
                                {
                                    val = double.NaN;
                                }
                                break;
                            }
                    }
                }
            }
            curVal = val.ToString();

            hasBeenTotaled = true; // flag the next number to clear the input box

            input.Clear();

            RenderOutput("=" + curVal, true);
        }

        private void btnNegPos_Click(object sender, RoutedEventArgs e)
        {
            RenderOutput((double.Parse(txtDisplay.Text) * -1).ToString());
        }

        #endregion

        #region Numbers
        private void btn0_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "0");
        }

        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "1");
        }

        private void btn2_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "2");
        }

        private void btn3_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "3");
        }

        private void btn4_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "4");
        }

        private void btn5_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "5");
        }

        private void btn6_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "6");
        }

        private void btn7_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "7");
        }

        private void btn8_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                RenderOutput("");
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "8");
        }

        private void btn9_Click(object sender, RoutedEventArgs e)
        {
            if (hasBeenTotaled)
            {
                //input.Push(txtDisplay.Text);
                txtDisplay.Text = "";
                hasBeenTotaled = false;
            }
            RenderOutput(txtDisplay.Text + "9");
        }

        private void btnDecimal_Click(object sender, RoutedEventArgs e)
        {
            // only allow one decimal
            bool hasDecimal = false;
            Stack<string>.Enumerator stackChecker = input.GetEnumerator();
            while (stackChecker.MoveNext())
            {
                if (stackChecker.Current == "")
                {
                    hasDecimal = true;
                    break;
                }
            }
            if (hasDecimal)
            {
                return;
            }
            else
            {
                RenderOutput(txtDisplay.Text + ".");
            }
        }

        #endregion

        #region Financial Stuff

        private void ToggleFinancial(object sender, RoutedEventArgs e)
        {
            if (pnlFinancial.Visibility == Visibility.Collapsed)
            {
                grdButtons.Height = MIN_CALC_HEIGHT;
                grdButtons.Margin = new Thickness(0, 23, 0, 0);
                pnlFinancial.Visibility = Visibility.Visible;
            }
            else
            {
                grdButtons.Height = MAX_CALC_HEIGHT;
                grdButtons.Margin = new Thickness(0, 0, 0, 0);
                pnlFinancial.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// V = D(1 + r/365)^365n
        /// D is the amount deposited, r is the interest rate,
        /// n is the number of years, and V is the final amount.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnDCI_Click(object sender, RoutedEventArgs e)
        {
            double D = double.MinValue;
            double.TryParse(txtDeposit.Text, out D);
            if (D == double.MinValue)
            {
                RenderTape("ERROR");
                return;
            }
            double r = double.MinValue;
            double.TryParse(txtRate.Text, out r);
            if (r == double.MinValue)
            {
                RenderTape("ERROR");
                return;
            }

            int n = int.MinValue;
            int.TryParse(txtYears.Text, out n);
            if (n == int.MinValue)
            {
                RenderTape("ERROR");
                return;
            }
            double pow = n * 365;
            double val = D * Math.Pow((1 + r / 365), pow);
            RenderOutput(val.ToString());
        }

        /// <summary>
        /// TP = nP = nB(r/12) / [ 1 - 1/(1+r/12)^n ]
        /// B is the amount borrowed, r is the interest rate, n is the number of months, and TP is the total payment.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTP_Click(object sender, RoutedEventArgs e)
        {
            double B = double.MinValue;
            double.TryParse(txtBorrowed.Text, out B);
            if (B == double.MinValue)
            {
                RenderTape("ERROR");
                return;
            }

            double r = double.MinValue;
            double.TryParse(txtRate.Text, out r);
            if (r == double.MinValue)
            {
                RenderTape("ERROR");
                return;
            }

            int n = int.MinValue;
            int.TryParse(txtYears.Text, out n);
            if (n == int.MinValue)
            {
                RenderTape("ERROR");
                return;
            }

            double val = (((n * B) * r / 12)) / (1 - (1 / Math.Pow(1 + (r / 12), n)));
            RenderOutput(val.ToString());
        }

        /// <summary>
        /// V = D(1+r)^n
        /// D is the amount deposited, r is the interest rate,
        /// n is the number of years, and V is the final amount.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSAI_Click(object sender, RoutedEventArgs e)
        {
            double D = double.MinValue;
            double.TryParse(txtDeposit.Text, out D);
            if (D == double.MinValue)
            {
                RenderTape("ERROR");
                return;
            }
            double r = double.MinValue;
            double.TryParse(txtRate.Text, out r);
            if (r == double.MinValue)
            {
                RenderTape("ERROR");
                return;
            }

            int n = int.MinValue;
            int.TryParse(txtYears.Text, out n);
            if (n == int.MinValue)
            {
                RenderTape("ERROR");
                return;
            }
            double val = D * Math.Pow((1 + r), n);
            RenderOutput(val.ToString());

        }
        #endregion
    }
}